package edu.neumont.pro180._2009_q2.chess;

import java.awt.Point;
import java.util.ArrayList;
import java.util.Random;
import java.util.Scanner;

import edu.neumont.pro180._2009_q2.Board;
import edu.neumont.pro180._2009_q2.BoardGame;
import edu.neumont.pro180._2009_q2.Player;
import edu.neumont.pro180._2009_q2.Square;
import edu.neumont.pro180._2009_q2.chess.pieceFactory.BishopFactory;
import edu.neumont.pro180._2009_q2.chess.pieceFactory.KingFactory;
import edu.neumont.pro180._2009_q2.chess.pieceFactory.KnightFactory;
import edu.neumont.pro180._2009_q2.chess.pieceFactory.PawnFactory;
import edu.neumont.pro180._2009_q2.chess.pieceFactory.PlayerFactory;
import edu.neumont.pro180._2009_q2.chess.pieceFactory.QueenFactory;
import edu.neumont.pro180._2009_q2.chess.pieceFactory.RookFactory;
import edu.neumont.pro180._2009_q2.interfaces.ICommand;
import edu.neumont.pro180._2009_q2.interfaces.IGamePiece;

public class ChessGame extends BoardGame {

	public ChessGame() {
		this.unselectedPieceViews.put(King.class, "k");
		this.selectedPieceViews.put(King.class, "K");
		this.unselectedPieceViews.put(Queen.class, "q");
		this.selectedPieceViews.put(Queen.class, "Q");
		this.unselectedPieceViews.put(Rook.class, "r");
		this.selectedPieceViews.put(Rook.class, "R");
		this.unselectedPieceViews.put(Bishop.class, "b");
		this.selectedPieceViews.put(Bishop.class, "B");
		this.unselectedPieceViews.put(Knight.class, "n");
		this.selectedPieceViews.put(Knight.class, "N");
		this.unselectedPieceViews.put(Pawn.class, "p");
		this.selectedPieceViews.put(Pawn.class, "P");

		this.rowCount = 8;
		this.columnCount = 8;
	}

	@Override
	public void playGame() throws IllegalStateException {
		try {
			this.board = new Board(this.rowCount, this.columnCount);
		} catch (IllegalArgumentException e) {
			throw new IllegalStateException(
					"Rows/Columns weren't set up correctly, cannot play game",
					e);
		}

		Player white = setUpPlayer("White");
		Player black = setUpPlayer("Black");
		board.addPlayer(white);
		board.addPlayer(black);
		populateChessBoard(board, white, black);
		board.setActivePlayer(white);

		this.currentSquare = this.board.getSquare(new Point(0, 0));
		Scanner scanner = new Scanner(System.in);
		do {
			printKey();
			printBoard(this.board);
			printInstructions();
		} while (processUserInput(scanner.nextLine()));
	}
	
	private static Player setUpPlayer(String playerName) {
		Scanner scanner = new Scanner(System.in);
		System.out.println("Is " + playerName + " automatic? y/n");
		
		Player player;
		// Using two different ways of instantiating objects, just as an example.
		if (scanner.next().toLowerCase().startsWith("y")) {
			// Uses the regular constructor
			player =  new RandomAIPlayer();
		} else {
			// Uses a factory to build the object.
			player = PlayerFactory.getInstance().create();
		}
		player.name = playerName;
		return player;
	}

	public boolean processUserInput(String command) {
		Player originalPlayer = board.getActivePlayer();
		boolean continueGame = super.processUserInput(command);
		boolean playerChanged = originalPlayer != board.getActivePlayer();
		if (continueGame && playerChanged) {
			continueGame = !isInCheckmate(originalPlayer, board);
		}
		return continueGame;
	}

	protected void populateChessBoard(Board board, Player white, Player black) {
		createPiecesOnSquares(new Point[] { new Point(0, 0), new Point(7, 0) },
				board, white, RookFactory.getInstance());
		createPiecesOnSquares(new Point[] { new Point(1, 0), new Point(6, 0) },
				board, white, KnightFactory.getInstance());
		createPiecesOnSquares(new Point[] { new Point(2, 0), new Point(5, 0) },
				board, white, BishopFactory.getInstance());
		createPiecesOnSquares(new Point[] { new Point(3, 0) }, board, white,
				QueenFactory.getInstance());
		createPiecesOnSquares(new Point[] { new Point(4, 0) }, board, white,
				KingFactory.getInstance());
		((PawnFactory) PawnFactory.getInstance()).setPawnDirection(new Point(0,
				1));
		createPiecesOnSquares(new Point[] { new Point(0, 1), new Point(1, 1),
				new Point(2, 1), new Point(3, 1), new Point(4, 1),
				new Point(5, 1), new Point(6, 1), new Point(7, 1) }, board,
				white, PawnFactory.getInstance());

		createPiecesOnSquares(new Point[] { new Point(0, 7), new Point(7, 7) },
				board, black, RookFactory.getInstance());
		createPiecesOnSquares(new Point[] { new Point(1, 7), new Point(6, 7) },
				board, black, KnightFactory.getInstance());
		createPiecesOnSquares(new Point[] { new Point(2, 7), new Point(5, 7) },
				board, black, BishopFactory.getInstance());
		createPiecesOnSquares(new Point[] { new Point(3, 7) }, board, black,
				QueenFactory.getInstance());
		createPiecesOnSquares(new Point[] { new Point(4, 7) }, board, black,
				KingFactory.getInstance());
		((PawnFactory) PawnFactory.getInstance()).setPawnDirection(new Point(0,
				-1));
		createPiecesOnSquares(new Point[] { new Point(0, 6), new Point(1, 6),
				new Point(2, 6), new Point(3, 6), new Point(4, 6),
				new Point(5, 6), new Point(6, 6), new Point(7, 6) }, board,
				black, PawnFactory.getInstance());
	}

	@Deprecated
	@Override
	protected void randomlyPopulateBoard(Board board, int totalPieces) {
		if (totalPieces >= board.getColumnCount() * board.getRowCount() - 1
				|| totalPieces < 1) {
			throw new IllegalArgumentException(
					"Pieces on board must be between 1, and the total number of squares - 1");
		}

		ArrayList<Square> validSquares = new ArrayList<Square>();
		for (int row = 0; row < board.getRowCount(); ++row) {
			for (int col = 0; col < board.getColumnCount(); ++col) {
				validSquares.add(board.getSquare(new Point(col, row)));
			}
		}

		Random random = new Random();
		int placedPieces = 0;
		while (placedPieces < totalPieces) {
			Square square = validSquares.get(random
					.nextInt(validSquares.size()));
			validSquares.remove(square);

			// Randomly select which type of piece goes on the board.
			switch (random.nextInt(5)) {
			case 0:
				new Bishop().setPosition(square);
				break;
			case 1:
				new Rook().setPosition(square);
				break;
			case 2:
				new Queen().setPosition(square);
				break;
			case 3:
				new King().setPosition(square);
				break;
			case 4:
				new Knight().setPosition(square);
				break;
			}

			++placedPieces;
		}
	}

	@Override
	protected void makeSelection() {
		Player player = board.getActivePlayer();
		if (currentSquare.hasGamePiece()) {
			player.setSelectedPiece(currentSquare.getGamePiece());
		}
		player.setPieceDestination(currentSquare);

		ICommand nextMove = player.getNextMove();
		if (nextMove != null) {
			board.doMove(nextMove);
			if (!isInCheck(player, board)) {
				board.nextPlayer();
			} else {
				System.out.println("You'd be in check if you made that move.");
				board.undoMove(nextMove);
			}
		}

		// if (selectedPiece != null) {
		// if (selectedPiece.getPossibleMoves().contains(currentSquare)) {
		// ICommand move = new Move(selectedPiece, currentSquare);
		// this.board.doMove(move);
		// if (!isInCheck(board.getActivePlayer(), board)) {
		// board.nextPlayer();
		// } else {
		// System.out.print("That move would put you in check!");
		// // commands.pop().undo();
		// board.undoMove(move);
		// }
		// }
		// selectedPiece = null;
		// } else if (currentSquare.hasGamePiece()) {
		// // Ensure the piece being selected belongs to the current player
		// if (board.getActivePlayer().getActivePieces().contains(
		// currentSquare.getGamePiece())) {
		// selectedPiece = currentSquare.getGamePiece();
		// }
		// }
	}

	/**
	 * 
	 * @param player
	 *            The player who might be in check.
	 * @param board
	 *            The game board being played on.
	 * @return whether the player is in check
	 */
	public boolean isInCheck(Player player, Board board) {
		boolean isCheck = false;

		// Find the player's king
		IGamePiece king = null;
		for (IGamePiece piece : player.getActivePieces()) {
			if (piece instanceof King) {
				king = piece;
				break;
			}
		}
		Square kingSquare = king.getSquare();

		opponentLoop: for (Player opponent : board.getPlayerCollection()) {
			if (opponent != player) {
				// Get the opposing team's pieces
				for (IGamePiece opposingPiece : opponent.getActivePieces()) {
					// See if any of the opposing pieces can move to the King's
					// square
					if (opposingPiece.getPossibleSquares().contains(kingSquare)) {
						isCheck = true;
						break opponentLoop;
					}
				}
			}
		}
		return isCheck;
	}

	public boolean isInCheckmate(Player player, Board board) {
		boolean isCheckmate = true;

		// Test all possible moves to see if that move will get the King out of
		// check.
		ICommand moveTest;
		pieceLoop: for (IGamePiece piece : player.getActivePieces()) {
			for (Square square : piece.getPossibleSquares()) {
				moveTest = piece.getMove(square);

				moveTest.execute();
				if (!isInCheck(player, board)) {
					// A move exists that will save the King from check.
					isCheckmate = false;
				}
				moveTest.undo();

				if (!isCheckmate) {
					break pieceLoop;
				}
			}
		}
		return isCheckmate;
	}
	
	public static Point coordsToPoint( String coords )
	{
		int x = (int)(coords.toLowerCase().charAt(0) - 'a');
		int y = (int)(coords.charAt(1) - '1');
		
		return new Point(x,y);
	}

	
	public static void main(String[] args) {
		ChessGame game = new ChessGame();
		game.playGame();
	}
}
